/**
  ******************************************************************************
  * @file    main_bean.c
  * @author  dragonchern
  * @version V1.000
  * @date    09-15-2018
  * @project linux client
  ******************************************************************************
  * @attention 因为使用环境的问题，该程序只能通过参数启动，不应通过配置文件启动，可使用
  *启动脚本来进行管理,代码中出现的e.g &sodf12sadfos@#,定义为注释指纹，方便用来定位对应
  *的注释
  * <h2><center>&copy; COPYRIGHT 2018 上海今中网络科技有限公司 </center></h2>
  ******************************************************************************
  */

#include "log-util.h"
#include "clientconfig.h"
#include "main_bean.h"

int          ServerStop       = FALSE;
BOOL         ServerLoop       = FALSE;
BOOL         ServerAlive      = TRUE;
int          EventSelect      = 1;
int          HeartbeatTime    = 0;
int          HeartbeatTimeout = 0;



int start_master_process(int argc, char ** argv)
{
    int retval              = RET_SUCCESS;
//    const char * certPath   = "./certs/postest3.cer";   // / 1.cer


    /*process init*/
    //进程后台话
	assert_param(0 == daemonize());
    MSG( LOG_DEBUG, "[start_master_process] process id ## %d",(int)getpid() );

    close_log();//close log before open
    open_log();

    //init clientConfig
    CLIENT_CONFIG_T  clientConfig;
    CLIENT_CONFIG_INIT( &clientConfig );

//	BOOL    ServerLoop;
//	int     ServerStop;
//  int     EventSelect;  //select read or write, 1 means write and 0 means read
    fd_set              rdfd;
	fd_set              wtfd;
	struct timeval      intervalTime;

	X509 * userCert         = NULL;
	X509_CERT_SUBJECT_T    x509_cert_subject;

    //init the clientInfo
    CLIENT_INFO_T clientInfo;
    clientInfo.port             = (&clientConfig)->serverPort;
    clientInfo.connectTimeout   = 5;
    clientInfo.serverFd         = 0;
    strcpy(clientInfo.serverIp, (&clientConfig)->serverIp);

    assert( NULL != ( userCert = x509_init_cert( (&clientConfig)->certPath) ) );

    clientInfo.x509Cert.context = x509_cert_context( (&clientConfig)->certPath, &clientInfo.x509Cert.length );
    clientInfo.serialNumber = x509_cert_sn( userCert );


    ///if you want get the bindinfo you need get the cert subject elements first
    assert( 0 == x509_cert_subject_ele(userCert, &x509_cert_subject) );
    clientInfo.bindInfo = x509_cert_bindinfo( userCert, &x509_cert_subject );
    assert( NULL != clientInfo.bindInfo );

    clientInfo.serverFd = tcp_connect( clientInfo.serverIp, clientInfo.port,clientInfo.connectTimeout );
    MSG( LOG_DEBUG, "[start_master_process] tcp connect ip ## %s port ## %d", clientInfo.serverIp, clientInfo.port );

	if (clientInfo.serverFd < 0)
	{
        MSG( LOG_DEBUG, "[start_master_process] tcp connect failed!");
        retval = RET_FAILED;         //attion: to release the serverFd
		goto label_finish;
	}
	/// 设置非阻塞
	if (setnonblocking(clientInfo.serverFd) < 0)
	{
        MSG( LOG_DEBUG, "[start_master_process] setnonblocking failed!");
	    retval = RET_FAILED;
		goto label_finish;
	}

	/// 开启循环事件
	ServerLoop                = TRUE;
	ServerStop                = 0;
    EventSelect               = 1;
	clientInfo.relayData.head = IDX_VPN_WELCOME;

///尝试对程序的结构做一些简单的改变，暂时没有做
//	void(*select_send[16])(void);
//	select_send[IDX_VPN_WELCOME] =
//	void(*select_recv[16])(void);


	while (ServerLoop)
	{
		FD_ZERO(&rdfd);
		FD_ZERO(&wtfd);
		if (EventSelect)
        {
            // 启动写事件
            FD_SET(clientInfo.serverFd, &wtfd);
        }
		else
        {   // 启动读事件
            FD_SET(clientInfo.serverFd, &rdfd);
        }

        intervalTime.tv_sec  = 1;
        intervalTime.tv_usec = 0;

		if ((retval = select(clientInfo.serverFd+1, &rdfd, &wtfd, NULL, &intervalTime)) < 0)
		{
			goto label_finish;
		}
		else if (retval == 0)
        {
            continue;
        }

		// 客户端写数据
		if (FD_ISSET(clientInfo.serverFd, &wtfd))
		{
			switch (clientInfo.relayData.head)
			{
				case IDX_VPN_WELCOME :
					if (send_request_of_welcome(&clientInfo) < 0)
						goto label_finish;
					break;

				case IDX_VPN_VERSION :
					if ((retval = send_request_of_version(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

                case IDX_VPN_X509 :
					if ((retval = send_request_of_x509(&clientInfo)) < 0)
					{
						free(clientInfo.fileInfo.buffer);
                        clientInfo.fileInfo.buffer   = NULL;
                        clientInfo.fileInfo.length   = 0;
                        clientInfo.fileInfo.position = 0;
                        MSG( LOG_DEBUG, "[start_master_process] selected wtfd cert verify failed! retval is %d", retval);
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					free(clientInfo.fileInfo.buffer);
					clientInfo.fileInfo.buffer   = NULL;
					clientInfo.fileInfo.length   = 0;
					clientInfo.fileInfo.position = 0;
					break;

				case IDX_VPN_SN :
					if ((retval = send_request_of_sn(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

				case IDX_VPN_BIND :
					if ((retval = send_request_of_bind(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

                /*case IDX_VPN_APPTYPE :
					if ((retval = send_request_of_apptype(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;*/

                case IDX_VPN_OPENVPN :
					if ((retval = send_request_of_openvpn(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

				/*case IDX_VPN_STUNNEL :
					if ((retval = send_request_of_stunnel(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

				case IDX_VPN_UTUNNEL :
					if ((retval = send_request_of_utunnel(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break; */

				case IDX_VPN_HEARTBEAT_TIMEOUT :
					if ((retval = send_request_of_heartbeat_timeout(&clientInfo)) <0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

				case IDX_VPN_HEARTBEAT :
					if ((retval = send_request_of_heartbeat(&clientInfo)) <0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

                default :
                	goto label_finish;
			} // end of switch
			EventSelect = 0;   // 0 read event
		} // end of if

		// 客户端读数据
		if (FD_ISSET(clientInfo.serverFd, &rdfd))
		{
			switch (clientInfo.relayData.head)
			{
			    case IDX_VPN_WELCOME :
					if (receive_result_of_welcome(&clientInfo) < 0)
						goto label_finish;
					break;

				case IDX_VPN_VERSION :
					if ((retval = receive_result_of_version(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					else if (retval == 2)
					{
					    continue;
					    //
						//goto label_finish;
						//retval = ::need to update
					}
					break;

 				case IDX_VPN_X509 :
					if ((retval = receive_result_of_x509(&clientInfo)) < 0)
					{
					    MSG( LOG_DEBUG, "[start_master_process] selected rdfd cert verify failed! retval is %d", retval);
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

                case IDX_VPN_SN :
					if ((retval = receive_result_of_sn(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

                case IDX_VPN_BIND :
					if ((retval = receive_result_of_bind(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

                /*case IDX_VPN_APPTYPE :
					if ((retval = receive_result_of_apptype(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;*/

                case IDX_VPN_OPENVPN :
					if ( (retval = receive_result_of_openvpn(&clientInfo, &clientConfig)) < 0 )
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

				/*case IDX_VPN_STUNNEL :
					if ((retval = receive_result_of_stunnel(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

				case IDX_VPN_UTUNNEL :
					if ((retval = receive_result_of_utunnel(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break; */

				case IDX_VPN_HEARTBEAT_TIMEOUT :
					if ((retval = receive_result_of_heartbeat_timeout(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

				case IDX_VPN_HEARTBEAT :
					if ((retval = receive_result_of_heartbeat(&clientInfo)) < 0)
					{
						goto label_finish;
					}
					else if (retval == 1)
					{
						continue;
					}
					break;

				default :
				    goto label_finish;
			}  // end of switch
			EventSelect = 1;
		}  // end of if

	}  // end of while

label_finish:
    ServerLoop = FALSE;

    MSG( LOG_DEBUG, "[start_master_process] server loop terminated!");
    /// 关闭socket
	if (clientInfo.serverFd > 0)
	{
		close(clientInfo.serverFd);
		clientInfo.serverFd = 0;
	}

    if(userCert)
    {
        x509_free_cert(userCert);
    }
    if(clientInfo.x509Cert.context)
    {
        free(clientInfo.x509Cert.context);
    }
    if(clientInfo.serialNumber)
    {
        free(clientInfo.serialNumber);
    }
    if(clientInfo.bindInfo)
    {
        free(clientInfo.bindInfo);
    }


    CLIENT_CONFIG_FREE( &clientConfig );     //free clientConfig but not clientInfo!
    system( "pkill sonicomEncrypt & pkill sonicomSecure" );
    close_log();

    return retval;
}

/**
  * @brief   show version 显示版本信息
  * @param   arg1 客户端信息结构体
  * @retval  成功返回0，失败返回-1，发送未完成返回1
  */
void show_version()
{
    printf("%s version : %s \n", LINUX_CLIENT_NAME, LINUX_CLIENT_VERSION);
}

/**
  * @brief  show help用于命令行解析
  * @param   arg1 客户端信息结构体
  * @retval  成功返回0，失败返回-1，发送未完成返回1
  */
void show_help()
{
    MSG( LOG_DEBUG, "[DEBUG] show help\n" );
}

/**
  * @brief   停止主要进程 命令行解析
  * @param   arg1 客户端信息结构体
  * @retval  成功返回0，失败返回-1，发送未完成返回1
  */
void stop_master_process()
{
    MSG( LOG_DEBUG, "[DEBUG] stop master process\n" );
}




